﻿/******************************************************************************
 * 【本类功能概述】                                 					      *
 *  版权所有（C）2017-20XX，米兔网络                                         *
 *  保留所有权利。                                                            *
 ******************************************************************************
 *  作者 : <Jiang Jiang>
 *  创建时间: 2018-3-28 11:21:35
 *  文件描述: 逻辑定时器类，支持timeScale
 *****************************************************************************/

using System;
using System.Collections.Generic;
using UnityEngine;

public class Timer : MonoBehaviour
{
    private static Timer _ins;
    public static GameObject TimeObj;

    public static Timer Instance
    {
        get
        {
            if (_ins == null)
            {
                TimeObj = new GameObject("Time");
                DontDestroyOnLoad(TimeObj);
                _ins = TimeObj.AddComponent<Timer>();
            }
            return _ins;
        }
    }

    public class TimerProc
    {
        public float duration;
        public float time;
        public int loop = -1;
        public bool beScale = true;
        public Action Call = delegate { };

        public void SubTime(float deltaTime, float noScaleTime)
        {
            float t = beScale ? deltaTime : noScaleTime;
            this.time -= t;
        }
    }

    private void Awake()
    {
        list = new List<TimerProc>();
        addList = new List<TimerProc>();
        removeList = new List<TimerProc>();
        Fixedlist = new List<TimerProc>();
        FixedaddList = new List<TimerProc>();
        FixedremoveList = new List<TimerProc>();
    }

    public void Dispose()
    {
        list.Clear();
        addList.Clear();
        removeList.Clear();
        Fixedlist.Clear();
        FixedaddList.Clear();
        FixedremoveList.Clear();
    }

    private List<TimerProc> list = null;
    private List<TimerProc> addList = null;
    private List<TimerProc> removeList = null;

    public TimerProc Add(float duration, int loop, Action call, bool beScale)
    {
        TimerProc proc = new TimerProc();
        proc.duration = duration;
        proc.time = duration;
        proc.loop = loop;
        proc.Call = call;
        proc.beScale = beScale;
        addList.Add(proc);

        return proc;
    }

    public TimerProc Add(float duration, int loop, Action call)
    {
        return Add(duration, loop, call, false);
    }

    public TimerProc Add(float duration, List<Action> call)
    {
        TimerProc proc = null;
        int id = 0;
        proc = Add(duration, call.Count, () =>
        {
            call[id].Invoke();
            id++;
        });
        return proc;
    }

    public void Reset(object timer, float duration, int loop, Action call, bool beScale)
    {
        TimerProc proc = timer as TimerProc;
        proc.duration = duration;
        proc.time = duration;
        proc.loop = loop;
        proc.Call = call;
        proc.beScale = beScale;

        if (!list.Contains(proc))
        {
            addList.Add(proc);
        }
    }

    public void Reset(object timer, float duration, int loop)
    {
        TimerProc proc = timer as TimerProc;
        proc.duration = duration;
        proc.time = duration;
        proc.loop = loop;

        if (!list.Contains(proc))
        {
            addList.Add(proc);
        }
    }

    public void Remove(object proc)
    {
        if (proc == null)
        {
            return;
        }

        TimerProc tp = proc as TimerProc;
        removeList.Add(tp);
    }

    public void Update()
    {
        float deltaTime = Time.deltaTime;
        float fixTime = Time.unscaledDeltaTime;
        DelayAddTimer();
        DelayRemoveTimer();

        for (int i = list.Count - 1; i >= 0; i--)
        {
            TimerProc proc = list[i];
            proc.SubTime(deltaTime, fixTime);

            if (proc.time <= 0)
            {
                try
                {
                    proc.Call();
                }
                catch (Exception e)
                {
                    list.RemoveAt(i);
                    //Debugger.LogException("timer call exception:", e);
                    debug.log(true, debug.Level.error_3, "事件", proc.Call, "\n -------\n", e);
                    continue;
                }

                if (proc.loop > 0)
                {
                    --proc.loop;
                    proc.time += proc.duration;
                }

                if (proc.loop == 0)
                {
                    list.RemoveAt(i);
                }
                else if (proc.loop < 0)
                {
                    proc.time += proc.duration;
                }
            }
        }
    }

    private void LateUpdate()
    {
        debug.ShowCalculate();
    }

    private void DelayAddTimer()
    {
        if (addList.Count > 0)
        {
            list.AddRange(addList);
            addList.Clear();
        }
    }

    private void DelayRemoveTimer()
    {
        if (removeList.Count == 0)
        {
            return;
        }

        for (int i = 0; i < removeList.Count; i++)
        {
            list.Remove(removeList[i]);
        }

        removeList.Clear();
    }

    //------------------------------------------------------------------------------------------------------
    private List<TimerProc> Fixedlist = null;

    private List<TimerProc> FixedaddList = null;
    private List<TimerProc> FixedremoveList = null;

    public TimerProc AddFixed(float duration, int loop, Action call, bool beScale)
    {
        TimerProc proc = new TimerProc();
        proc.duration = duration;
        proc.time = duration;
        proc.loop = loop;
        proc.Call = call;
        proc.beScale = beScale;
        FixedaddList.Add(proc);

        return proc;
    }

    public TimerProc AddFixed(float duration, int loop, Action call)
    {
        return AddFixed(duration, loop, call, false);
    }

    public TimerProc AddFixed(float duration, List<Action> call)
    {
        TimerProc proc = null;
        int id = 0;
        proc = AddFixed(duration, call.Count, () =>
        {
            call[id].Invoke();
            id++;
        });
        return proc;
    }

    public void ResetFixed(object timer, float duration, int loop, Action call, bool beScale)
    {
        TimerProc proc = timer as TimerProc;
        proc.duration = duration;
        proc.time = duration;
        proc.loop = loop;
        proc.Call = call;
        proc.beScale = beScale;

        if (!Fixedlist.Contains(proc))
        {
            FixedaddList.Add(proc);
        }
    }

    public void ResetFixed(object timer, float duration, int loop)
    {
        TimerProc proc = timer as TimerProc;
        proc.duration = duration;
        proc.time = duration;
        proc.loop = loop;

        if (!Fixedlist.Contains(proc))
        {
            FixedaddList.Add(proc);
        }
    }

    public void RemoveFixed(object proc)
    {
        if (proc == null)
        {
            return;
        }

        TimerProc tp = proc as TimerProc;
        FixedremoveList.Add(tp);
    }

    public void FixedUpdate()
    {
        float deltaTime = Time.fixedDeltaTime;
        float fixTime = Time.fixedUnscaledDeltaTime;
        DelayAddFixedTimer();
        DelayRemoveFixedTimer();

        for (int i = Fixedlist.Count - 1; i >= 0; i--)
        {
            TimerProc proc = Fixedlist[i];
            proc.SubTime(deltaTime, fixTime);

            if (proc.time <= 0)
            {
                try
                {
                    proc.Call();
                }
                catch (Exception e)
                {
                    Fixedlist.RemoveAt(i);
                    //Debugger.LogException("timer call exception:", e);
                    debug.log(true, debug.Level.error_3, "事件", proc.Call, "\n -------\n", e);
                    continue;
                }

                if (proc.loop > 0)
                {
                    --proc.loop;
                    proc.time += proc.duration;
                }

                if (proc.loop == 0)
                {
                    Fixedlist.RemoveAt(i);
                }
                else if (proc.loop < 0)
                {
                    proc.time += proc.duration;
                }
            }
        }
    }

    private void DelayAddFixedTimer()
    {
        if (FixedaddList.Count > 0)
        {
            Fixedlist.AddRange(FixedaddList);
            FixedaddList.Clear();
        }
    }

    private void DelayRemoveFixedTimer()
    {
        if (FixedremoveList.Count == 0)
        {
            return;
        }

        for (int i = 0; i < FixedremoveList.Count; i++)
        {
            Fixedlist.Remove(FixedremoveList[i]);
        }

        FixedremoveList.Clear();
    }
}